"""
MoBox Dashboard 更新管理 API
提供Docker镜像更新和课程内容更新的RESTful接口
"""

import os
import subprocess
import json
import asyncio
from datetime import datetime
from typing import Dict, List, Optional
from pathlib import Path

from fastapi import APIRouter, HTTPException, BackgroundTasks
from pydantic import BaseModel
import docker
import git

router = APIRouter()

# 配置
PROJECT_DIR = Path(__file__).parent.parent.parent
SCRIPTS_DIR = PROJECT_DIR / "scripts"
COMPOSE_FILE = PROJECT_DIR / "docker-compose.prod.yml"
LOG_DIR = PROJECT_DIR / "logs" / "updates"
ALIYUN_REGISTRY = "crpi-axr6nxua9k92sqiz.cn-hangzhou.personal.cr.aliyuncs.com/mobox-docker"

# 数据模型
class UpdateRequest(BaseModel):
    components: List[str] = ["all"]
    force: bool = False
    backup: bool = True
    skip_course: bool = False

class UpdateStatus(BaseModel):
    status: str
    message: str
    progress: float
    details: Dict

class ComponentStatus(BaseModel):
    name: str
    current_version: str
    latest_version: str
    needs_update: bool
    last_updated: Optional[str]

class SystemStatus(BaseModel):
    docker_services: List[Dict]
    images: List[Dict]
    course_status: Dict
    disk_usage: Dict
    last_update: Optional[str]

# 全局更新状态
update_progress = {
    "status": "idle",
    "progress": 0.0,
    "message": "",
    "details": {}
}

def run_script_command(command: List[str], cwd: str = None) -> Dict:
    """执行脚本命令并返回结果"""
    try:
        result = subprocess.run(
            command,
            cwd=cwd or PROJECT_DIR,
            capture_output=True,
            text=True,
            timeout=300
        )
        return {
            "success": result.returncode == 0,
            "stdout": result.stdout,
            "stderr": result.stderr,
            "returncode": result.returncode
        }
    except subprocess.TimeoutExpired:
        return {
            "success": False,
            "stdout": "",
            "stderr": "命令执行超时",
            "returncode": -1
        }
    except Exception as e:
        return {
            "success": False,
            "stdout": "",
            "stderr": str(e),
            "returncode": -1
        }

@router.get("/status", response_model=SystemStatus)
async def get_system_status():
    """获取系统当前状态"""
    try:
        # Docker客户端
        docker_client = docker.from_env()
        
        # 获取运行中的服务
        containers = docker_client.containers.list()
        docker_services = []
        for container in containers:
            if 'mobox' in container.name:
                docker_services.append({
                    "name": container.name,
                    "status": container.status,
                    "image": container.image.tags[0] if container.image.tags else "unknown",
                    "ports": container.ports
                })
        
        # 获取镜像信息
        images = []
        for service in ["blockly", "jupyter", "dashboard"]:
            try:
                image_name = f"{ALIYUN_REGISTRY}/{service}:latest"
                image = docker_client.images.get(image_name)
                images.append({
                    "name": service,
                    "image": image_name,
                    "id": image.id[:12],
                    "created": image.attrs.get("Created", ""),
                    "size": image.attrs.get("Size", 0)
                })
            except docker.errors.ImageNotFound:
                images.append({
                    "name": service,
                    "image": f"{ALIYUN_REGISTRY}/{service}:latest",
                    "id": "not found",
                    "created": "",
                    "size": 0
                })
        
        # 获取课程状态
        course_dir = PROJECT_DIR / "data" / "jupyter" / "course"
        course_status = {"exists": False, "last_commit": "", "last_update": ""}
        
        if course_dir.exists() and (course_dir / ".git").exists():
            try:
                repo = git.Repo(course_dir)
                course_status = {
                    "exists": True,
                    "last_commit": repo.head.commit.hexsha[:8],
                    "last_update": repo.head.commit.committed_datetime.isoformat(),
                    "branch": repo.active_branch.name
                }
            except Exception:
                course_status["exists"] = True
        
        # 获取磁盘使用情况
        disk_usage = {}
        try:
            result = subprocess.run(
                ["df", "-h", str(PROJECT_DIR)],
                capture_output=True,
                text=True
            )
            if result.returncode == 0:
                lines = result.stdout.strip().split('\n')
                if len(lines) > 1:
                    parts = lines[1].split()
                    disk_usage = {
                        "total": parts[1],
                        "used": parts[2],
                        "available": parts[3],
                        "usage_percent": parts[4]
                    }
        except Exception:
            pass
        
        # 获取最后更新时间
        last_update = None
        log_files = list(LOG_DIR.glob("update_*.log"))
        if log_files:
            latest_log = max(log_files, key=lambda x: x.stat().st_mtime)
            last_update = datetime.fromtimestamp(latest_log.stat().st_mtime).isoformat()
        
        return SystemStatus(
            docker_services=docker_services,
            images=images,
            course_status=course_status,
            disk_usage=disk_usage,
            last_update=last_update
        )
    
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"获取系统状态失败: {str(e)}")

@router.get("/check-updates")
async def check_updates():
    """检查可用更新"""
    try:
        # 执行检查命令
        result = run_script_command([
            str(SCRIPTS_DIR / "update-manager.sh"),
            "--check"
        ])
        
        if not result["success"]:
            raise HTTPException(status_code=500, detail=f"检查更新失败: {result['stderr']}")
        
        # 解析输出
        updates_available = {
            "docker_updates": [],
            "course_updates": False,
            "total_updates": 0
        }
        
        # 简单解析（实际项目中可能需要更复杂的解析）
        if "有新版本可用" in result["stdout"]:
            updates_available["total_updates"] += 1
        
        return updates_available
    
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"检查更新失败: {str(e)}")

@router.post("/update")
async def start_update(request: UpdateRequest, background_tasks: BackgroundTasks):
    """开始更新过程"""
    global update_progress
    
    if update_progress["status"] == "updating":
        raise HTTPException(status_code=409, detail="更新正在进行中")
    
    # 重置进度
    update_progress = {
        "status": "updating",
        "progress": 0.0,
        "message": "准备更新...",
        "details": {"components": request.components}
    }
    
    # 在后台执行更新
    background_tasks.add_task(perform_update, request)
    
    return {"message": "更新已开始", "status": "updating"}

async def perform_update(request: UpdateRequest):
    """执行更新任务"""
    global update_progress
    
    try:
        # 构建命令
        command = [str(SCRIPTS_DIR / "update-manager.sh")]
        
        if request.backup:
            command.append("--backup")
        
        if request.force:
            command.append("--force")
            
        if request.skip_course:
            command.append("--skip-course")
        
        command.extend(request.components)
        
        # 更新进度
        update_progress.update({
            "progress": 20.0,
            "message": "执行更新脚本..."
        })
        
        # 执行更新
        result = run_script_command(command)
        
        if result["success"]:
            update_progress.update({
                "status": "completed",
                "progress": 100.0,
                "message": "更新完成",
                "details": {
                    "components": request.components,
                    "stdout": result["stdout"]
                }
            })
        else:
            update_progress.update({
                "status": "failed",
                "progress": 0.0,
                "message": f"更新失败: {result['stderr']}",
                "details": {
                    "error": result["stderr"],
                    "stdout": result["stdout"]
                }
            })
    
    except Exception as e:
        update_progress.update({
            "status": "failed",
            "progress": 0.0,
            "message": f"更新异常: {str(e)}",
            "details": {"error": str(e)}
        })

@router.get("/update-progress", response_model=UpdateStatus)
async def get_update_progress():
    """获取更新进度"""
    return UpdateStatus(
        status=update_progress["status"],
        message=update_progress["message"],
        progress=update_progress["progress"],
        details=update_progress["details"]
    )

@router.post("/rollback")
async def rollback_system():
    """回滚系统到上一个版本"""
    try:
        result = run_script_command([
            str(SCRIPTS_DIR / "update-manager.sh"),
            "--rollback"
        ])
        
        if result["success"]:
            return {"message": "系统回滚成功"}
        else:
            raise HTTPException(status_code=500, detail=f"回滚失败: {result['stderr']}")
    
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"回滚失败: {str(e)}")

@router.get("/logs")
async def get_update_logs(limit: int = 50):
    """获取更新日志"""
    try:
        log_files = list(LOG_DIR.glob("update_*.log"))
        if not log_files:
            return {"logs": []}
        
        # 获取最新的日志文件
        latest_log = max(log_files, key=lambda x: x.stat().st_mtime)
        
        with open(latest_log, 'r', encoding='utf-8') as f:
            lines = f.readlines()
            recent_lines = lines[-limit:] if len(lines) > limit else lines
        
        return {
            "logs": [line.strip() for line in recent_lines],
            "log_file": str(latest_log.name),
            "total_lines": len(lines)
        }
    
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"获取日志失败: {str(e)}")

@router.delete("/cleanup")
async def cleanup_old_data():
    """清理旧数据和未使用的镜像"""
    try:
        # 清理Docker未使用的镜像
        docker_client = docker.from_env()
        docker_client.images.prune(filters={"dangling": True})
        
        # 清理旧的备份文件（保留最近5个）
        backup_dir = PROJECT_DIR / "backups"
        if backup_dir.exists():
            backup_folders = sorted(
                [d for d in backup_dir.iterdir() if d.is_dir()],
                key=lambda x: x.stat().st_mtime,
                reverse=True
            )
            
            # 删除超过5个的旧备份
            for old_backup in backup_folders[5:]:
                subprocess.run(["rm", "-rf", str(old_backup)])
        
        # 清理旧的日志文件（保留最近10个）
        log_files = sorted(
            LOG_DIR.glob("update_*.log"),
            key=lambda x: x.stat().st_mtime,
            reverse=True
        )
        
        for old_log in log_files[10:]:
            old_log.unlink()
        
        return {"message": "清理完成"}
    
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"清理失败: {str(e)}") 